package com.suray.system.run.seer.task;

import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.system.run.seer.action.SeerAgvActions;
import com.suray.system.run.seer.http.ToSeerAgv;
import com.suray.system.run.seer.http.bean.SeerAgvAddBlock;
import com.suray.system.run.seer.http.bean.SeerAgvMarkComplete;
import com.suray.system.run.seer.http.bean.SeerAgvTaskInquire;
import com.suray.system.run.seer.http.bean.SeerAgvTaskSend;
import com.suray.system.run.seer.mysql.SeerTaskDB;
import com.suray.system.run.seer.mysql.SeerTaskDBServiceImpl;
import com.suray.system.run.seer.type.SeerAgvBlockState;
import com.suray.system.run.seer.type.SeerAgvOperation;
import com.suray.system.run.seer.type.SeerAgvProcess;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Seer AGV 任务执行
 */
public class SeerTaskAction {

    SeerTaskDBServiceImpl seerTaskDBService = SpringContextHolder.getBean(SeerTaskDBServiceImpl.class);

    /**
     * 任务数据
     */
    private SeerTaskDB taskDB;

    /**
     * 任务状态
     */
    private volatile SeerAgvTaskInquire taskInquire;

    /**
     * 动作块id
     */
    private Map<SeerAgvProcess, String> blockIdMap;

    /**
     * 无特殊动作，默认无动作
     */
    private final static String defaultOper = SeerAgvOperation.JACK_WAIT.getAction();

    public SeerTaskAction(SeerTaskDB taskDB) {
        this.taskDB = taskDB;
        generalBlockId();
    }

    public void setTaskInquire(SeerAgvTaskInquire taskInquire) {
        this.taskInquire = taskInquire;
    }

    /**
     * 生成动作块 id
     */
    private void generalBlockId() {
        blockIdMap = new HashMap<>();
        String taskId = taskDB.getTaskId();

        blockIdMap.put(SeerAgvProcess.SEND_TASK, taskId + "B1");
        blockIdMap.put(SeerAgvProcess.SEND_GET_CARGO, taskId + "B2");
        blockIdMap.put(SeerAgvProcess.SEND_OUT_START, taskId + "B3");
        blockIdMap.put(SeerAgvProcess.SEND_RUN_TO_OUT_END, taskId + "B4");
        blockIdMap.put(SeerAgvProcess.SEND_PUT_CARGO, taskId + "B5");
        blockIdMap.put(SeerAgvProcess.SEND_OUT_END, taskId + "B6");
    }

    public SeerTaskDB getTaskDB() {
        return taskDB;
    }

    /**
     * 任务是否结束
     * @return
     */
    public boolean isEnd() {
        return taskDB.getProcess() == SeerAgvProcess.END;
    }

    public void init() {
        LoggerUtil.info("Seer AGV任务开始执行:" + taskDB, this.getClass());
    }

    public void run() {
        switch (taskDB.getProcess()) {
            case INIT:
                init();
                updateProcess(SeerAgvProcess.SEND_TASK);
            case SEND_TASK:
                sendTask();
                break;
            case WAIT_START_OK:
                waitStartOk();
                break;
            case SEND_GET_CARGO:
                sendGetCargo();
                break;
            case SEND_OUT_START:
                sendOutStart();
                break;
            case SEND_RUN_TO_OUT_END:
                sendRunToOutEnd();
                break;
            case WAIT_GET_CARGO:
                waitGetCargo();
                break;
            case WAIT_OUT_START:
                waitOutStart();
                break;
            case WAIT_END_OK:
                waitEndOk();
                break;
            case SEND_PUT_CARGO:
                sendPutCargo();
                break;
            case SEND_OUT_END:
                sendOutEnd();
                break;
            case SEND_TASK_CLOSE:
                sendTaskClose();
                break;
            case WAIT_PUT_CARGO:
                waitPutCargo();
                break;
            case WAIT_OUT_END:
                waitOutEnd();
                break;
            case WAIT_TASK_END:
                waitTaskEnd();
                break;
            case END:
                end();
            default:
                break;
        }
    }

    /**
     * 发送任务
     */
    private void sendTask() {
        SeerAgvTaskSend taskSend = new SeerAgvTaskSend(taskDB.getTaskId());
        taskSend.addBlock(blockIdMap.get(SeerAgvProcess.SEND_TASK),
                taskDB.getStartOutPoint(), defaultOper);
        taskSend.setComplete(false);

        if (ToSeerAgv.seerSendTask(taskSend)) {
            updateProcess(SeerAgvProcess.WAIT_START_OK);
        }
    }

    /**
     * 等待起点允许进入取货
     */
    private void waitStartOk() {
        if (SeerAgvActions.askGetCargoInPoint(taskDB.getStartPoint(), this)) {
            updateProcess(SeerAgvProcess.SEND_GET_CARGO);
        }
    }

    /**
     * 发送进入起点取货
     */
    private void sendGetCargo() {
        SeerAgvAddBlock addBlock = new SeerAgvAddBlock(taskDB.getTaskId());
        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_GET_CARGO),
                taskDB.getStartPoint(), taskDB.getStartOperation());

        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_OUT_START),
                taskDB.getStartOutPoint(), defaultOper);

        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_RUN_TO_OUT_END),
                taskDB.getEndOutPoint(), defaultOper);

        if (ToSeerAgv.addBlock(addBlock)) {
            updateProcess(SeerAgvProcess.WAIT_GET_CARGO);
        }
    }

    /**
     * 发送离开起点
     */
    private void sendOutStart() {
        SeerAgvAddBlock addBlock = new SeerAgvAddBlock(taskDB.getTaskId());
        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_OUT_START),
                taskDB.getStartOutPoint(), defaultOper);

        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_RUN_TO_OUT_END),
                taskDB.getEndOutPoint(), defaultOper);

        if (ToSeerAgv.addBlock(addBlock)) {
            updateProcess(SeerAgvProcess.WAIT_GET_CARGO);
        }
    }

    /**
     * 发送前往放货点外面
     */
    private void sendRunToOutEnd() {
        SeerAgvAddBlock addBlock = new SeerAgvAddBlock(taskDB.getTaskId());
        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_RUN_TO_OUT_END),
                taskDB.getEndOutPoint(), defaultOper);

        if (ToSeerAgv.addBlock(addBlock)) {
            updateProcess(SeerAgvProcess.WAIT_GET_CARGO);
        }
    }

    /**
     * 等待取货完成
     */
    private void waitGetCargo() {
        if (checkBlockFinish(blockIdMap.get(SeerAgvProcess.SEND_GET_CARGO))) {
            if (SeerAgvActions.askChangeCargoState(taskDB.getStartPoint(), false)) {
                updateProcess(SeerAgvProcess.WAIT_OUT_START);
            } else {
                LoggerUtil.warn(taskDB.getStartPoint() + "货物状态修改无货失败", this.getClass());
            }
        }
    }

    /**
     * 等待离开取货点
     */
    private void waitOutStart() {
        if (checkBlockFinish(blockIdMap.get(SeerAgvProcess.SEND_OUT_START))) {
            if (SeerAgvActions.askLeavePoint(taskDB.getStartPoint(), this)) {
                updateProcess(SeerAgvProcess.WAIT_END_OK);
            }
        }
    }

    /**
     * 等待放货点允许进入放货
     */
    private void waitEndOk() {
        if (SeerAgvActions.askPutCargoInPoint(taskDB.getEndPoint(), this)) {
            updateProcess(SeerAgvProcess.SEND_PUT_CARGO);
        }
    }

    /**
     * 发送进入放货点放货
     */
    private void sendPutCargo() {
        SeerAgvAddBlock addBlock = new SeerAgvAddBlock(taskDB.getTaskId());
        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_PUT_CARGO),
                taskDB.getEndPoint(), taskDB.getEndOperation());

        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_OUT_END),
                taskDB.getEndOutPoint(), defaultOper);

        if (ToSeerAgv.addBlock(addBlock)) {
            updateProcess(SeerAgvProcess.SEND_TASK_CLOSE);
        }
    }

    /**
     * 发送离开放货点
     */
    private void sendOutEnd() {
        SeerAgvAddBlock addBlock = new SeerAgvAddBlock(taskDB.getTaskId());
        addBlock.addBlock(blockIdMap.get(SeerAgvProcess.SEND_OUT_END),
                taskDB.getEndOutPoint(), defaultOper);

        if (ToSeerAgv.addBlock(addBlock)) {
            updateProcess(SeerAgvProcess.SEND_TASK_CLOSE);
        }
    }

    /**
     * 发送任务封口
     */
    private void sendTaskClose() {
        SeerAgvMarkComplete markComplete = new SeerAgvMarkComplete();
        markComplete.setId(taskDB.getTaskId());

        if (ToSeerAgv.markComplete(markComplete)) {
            updateProcess(SeerAgvProcess.WAIT_PUT_CARGO);
        }
    }

    /**
     * 等待放货完成
     */
    private void waitPutCargo() {
        if (checkBlockFinish(blockIdMap.get(SeerAgvProcess.SEND_GET_CARGO))) {
            if (SeerAgvActions.askChangeCargoState(taskDB.getEndPoint(), true)) {
                updateProcess(SeerAgvProcess.WAIT_OUT_END);
            } else {
                LoggerUtil.warn(taskDB.getStartPoint() + "货物状态修改有货失败", this.getClass());
            }
        }
    }

    /**
     * 等待离开放货点
     */
    private void waitOutEnd() {
        if (checkBlockFinish(blockIdMap.get(SeerAgvProcess.SEND_OUT_END))) {
            if (SeerAgvActions.askLeavePoint(taskDB.getStartPoint(), this)) {
                updateProcess(SeerAgvProcess.WAIT_TASK_END);
            }
        }
    }

    /**
     * 等待任务结束
     */
    private void waitTaskEnd() {
        if (taskInquire.getState().equals(SeerAgvBlockState.TASK_FINISH.getDes())) {
            LoggerUtil.info("任务完成:" + taskDB, this.getClass());
            updateProcess(SeerAgvProcess.END);
        }
    }

    /**
     * 结束
     */
    private void end() {

    }

    /**
     * 检测动作块是否完成
     * @param blockId
     * @return
     */
    private boolean checkBlockFinish(String blockId) {
        if (taskInquire == null) {
            return false;
        }

        for (SeerAgvTaskInquire.BlocksDTO block : taskInquire.getBlocks()) {
            if (block.getBlockId().equals(blockId)) {
                if (block.getState().equals(SeerAgvBlockState.TASK_FINISH.getDes())) {
                    LoggerUtil.info("动作块完成：" + block, this.getClass());
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 更新任务进度
     */
    private void updateProcess(SeerAgvProcess process) {
        try {
            taskDB.setProcess(process);
            seerTaskDBService.updateById(taskDB);
        } catch (Exception e) {
            LoggerUtil.error("更新Seer Agv任务进度出错:" + taskDB, this.getClass());
            throw e;
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SeerTaskAction that = (SeerTaskAction) o;
        return Objects.equals(seerTaskDBService, that.seerTaskDBService) &&
                Objects.equals(taskDB, that.taskDB) &&
                Objects.equals(taskInquire, that.taskInquire) &&
                Objects.equals(blockIdMap, that.blockIdMap);
    }

    @Override
    public int hashCode() {
        return Objects.hash(seerTaskDBService, taskDB, taskInquire, blockIdMap);
    }

    @Override
    public String toString() {
        return taskDB.getTaskId();
    }
}
